// Copyright Epic Games, Inc. All Rights Reserved.

#include "LyraReticleWidgetBase.h"

#include "Inventory/LyraInventoryItemInstance.h"
#include "Weapons/LyraRangedWeaponInstance.h"
#include "Weapons/LyraWeaponInstance.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(LyraReticleWidgetBase)

/**
 * 构造函数
 * @param ObjectInitializer 对象初始化器
 */
ULyraReticleWidgetBase::ULyraReticleWidgetBase(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
}

/**
 * 从武器初始化
 * @param InWeapon 要初始化的武器实例
 */
void ULyraReticleWidgetBase::InitializeFromWeapon(ULyraWeaponInstance* InWeapon)
{
	WeaponInstance = InWeapon; //设置武器实例
	InventoryInstance = nullptr; //重置库存实例
	if (WeaponInstance) //如果武器实例有效
	{
		InventoryInstance = Cast<ULyraInventoryItemInstance>(WeaponInstance->GetInstigator()); //转换并设置库存实例
	}
	OnWeaponInitialized(); //调用武器初始化事件
}

/**
 * 计算扩散角度
 * @return 扩散角度（度）
 */
float ULyraReticleWidgetBase::ComputeSpreadAngle() const
{
	if (const ULyraRangedWeaponInstance* RangedWeapon = Cast<const ULyraRangedWeaponInstance>(WeaponInstance)) //如果是远程武器
	{
		const float BaseSpreadAngle = RangedWeapon->GetCalculatedSpreadAngle(); //获取计算的基础扩散角度
		const float SpreadAngleMultiplier = RangedWeapon->GetCalculatedSpreadAngleMultiplier(); //获取计算的扩散角度乘数
		const float ActualSpreadAngle = BaseSpreadAngle * SpreadAngleMultiplier; //计算实际扩散角度

		return ActualSpreadAngle; //返回实际扩散角度
	}
	else //如果不是远程武器
	{
		return 0.0f; //返回0
	}
}

/**
 * 是否具有首发精度
 * @return 是否具有首发精度
 */
bool ULyraReticleWidgetBase::HasFirstShotAccuracy() const
{
	if (const ULyraRangedWeaponInstance* RangedWeapon = Cast<const ULyraRangedWeaponInstance>(WeaponInstance)) //如果是远程武器
	{
		return RangedWeapon->HasFirstShotAccuracy(); //返回是否具有首发精度
	}
	else //如果不是远程武器
	{
		return false; //返回false
	}
}

/**
 * 计算最大屏幕空间扩散半径
 * @return 最大屏幕空间扩散半径（像素）
 */
float ULyraReticleWidgetBase::ComputeMaxScreenspaceSpreadRadius() const
{
	const float LongShotDistance = 10000.f; //长射击距离

	APlayerController* PC = GetOwningPlayer(); //获取拥有玩家控制器
	if (PC && PC->PlayerCameraManager) //如果玩家控制器和相机管理器有效
	{
		// 武器的扩散可以看作是一个圆锥形状。为了找到用于十字准星可视化的屏幕空间扩散，
		// 我们在长距离处创建一条在圆锥边缘的线。该点的末端位于圆锥圆的边缘上。
		// 然后我们将其投影回屏幕上。它距离屏幕中心的距离就是扩散半径。

		// 由于相机位置和枪口之间存在一些距离，这并不完美。

		const float SpreadRadiusRads = FMath::DegreesToRadians(ComputeSpreadAngle() * 0.5f); //计算扩散半径弧度值
		const float SpreadRadiusAtDistance = FMath::Tan(SpreadRadiusRads) * LongShotDistance; //计算在距离处的扩散半径

		FVector CamPos; //相机位置
		FRotator CamOrient; //相机方向
		PC->PlayerCameraManager->GetCameraViewPoint(CamPos, CamOrient); //获取相机视点

		FVector CamForwDir = CamOrient.RotateVector(FVector::ForwardVector); //相机前向方向
		FVector CamUpDir   = CamOrient.RotateVector(FVector::UpVector); //相机上方向

		FVector OffsetTargetAtDistance = CamPos + (CamForwDir * LongShotDistance) + (CamUpDir * SpreadRadiusAtDistance); //计算在距离处的偏移目标

		FVector2D OffsetTargetInScreenspace; //屏幕空间中的偏移目标

		if (PC->ProjectWorldLocationToScreen(OffsetTargetAtDistance, OffsetTargetInScreenspace, true)) //如果成功投影到屏幕
		{
			int32 ViewportSizeX(0), ViewportSizeY(0); //视口大小
			PC->GetViewportSize(ViewportSizeX, ViewportSizeY); //获取视口大小

			const FVector2D ScreenSpaceCenter(FVector::FReal(ViewportSizeX) * 0.5f, FVector::FReal(ViewportSizeY) * 0.5f); //屏幕空间中心

			return (OffsetTargetInScreenspace - ScreenSpaceCenter).Length(); //返回距离长度
		}
	}
	
	return 0.0f; //返回0
}